//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2011 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_ITERATOR_TRANSFORM_H_
#define _BUOLA_ITERATOR_TRANSFORM_H_

#include <buola/iterator/facade.h>
#include <buola/iterator/range.h>
#include <buola/functors/transform/unary.h>

namespace buola {

///\addtogroup iterators
///@{

///\cond
template<typename tSub,typename tFunc>
class ITransform;

namespace detail {

    template<typename tSub,typename tFunc>
    struct MIteratorTransform
    {
        typedef decltype(std::declval<const tFunc&>()(*std::declval<const tSub&>())) TReference;
        typedef IFacade<ITransform<tSub,tFunc>,typename std::iterator_traits<tSub>::iterator_category,
                  ORemoveRef<TReference>,TReference,typename std::iterator_traits<tSub>::difference_type> TFacade;
    };

/*namespace detail*/ }
///\endcond

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
/// iterator transform adaptor
///
/// Adapts another iterator (the underlying iterator) so that, when dereferencing, instead of returning the value
/// which would be returned by the underlying iterator, it returns the result of applying a function to that value.
///
/// \tparam tSub the underlying iterator type
/// \tparam tFunc the type of a unary function or function object. It must take as argument the type returned by
///         dereferencing the underlying operator and return a value
///
/// The result of applying the transformation can be of a different type than the one returned by the original
/// iterator
///
/// For an example, see the helper function \ref transform_range
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tSub,typename tFunc>
class ITransform : public detail::MIteratorTransform<tSub,tFunc>::TFacade
{
    typedef typename ITransform::IFacade TFacade;
    friend TFacade;

public:
    ///default constructor (invalid iterator)
    ITransform()=default;

    ///initializes the iterator from an underlying iterator \p pSub
    ITransform(const tSub &pSub,const tFunc &pFunc)
        :   mSub(pSub)
        ,   mFunc(pFunc)
    {}

    ///allows access to the underlying iterator
    const tSub &Sub()   {   return mSub;    }

private:
    ///returns the result of calling `mFunc` with the result of dereferencing `mSub`
    typename TFacade::reference Dereference() const
    {
        return mFunc(*mSub);
    }

    ///increments the underlying iterator
    void Increment()
    {
        ++mSub;
    }

    ///decrements the underlying iterator
    void Decrement()
    {
        --mSub;
    }

    ///advances the underlying iterator by \p pN
    void Advance(typename std::iterator_traits<tSub>::difference_type pN)
    {
        mSub+=pN;
    }

    ///returns true if the two underlying iterators contain the same value
    ///
    ///It is OK to use a generic template parameter, since it will only be called for compatible iterators.
    template<typename tO>
    bool Equal(const tO &pO) const
    {
        return mSub==pO.mSub;
    }

    ///returns the distance to some other underlying iterator
    ///
    ///It is OK to use a generic template parameter, since it will only be called for compatible iterators.
    template<typename tO>
    typename std::iterator_traits<tSub>::difference_type DistanceTo(const tO &pO) const
    {
        return pO.mSub-mSub;
    }

    tSub mSub;
    tFunc mFunc;
};

///\name Transform
//@{

///returns an \ref ITransform adapted iterator which performs a transformation on elements
///
///\param pSub the underlying iterator
///\param pFunc a unary function or function object which performs the transformation
///
///When the iterator is dereferenced, it will return the result of `pFunc(*pSub)`, where `*pSub` is the result of 
///dereferencing the underlying iterator
///
///Any other operation on this iterator is as if it was performed on the underlying iterator
template<typename tSub,typename tFunc>
inline ITransform<tSub,tFunc> transform_iterator(const tSub &pSub,const tFunc &pFunc)
{
    return {pSub,pFunc};
}

///returns an \ref ITransform object pointing to the beginning of the range \p pRange
///
///\param pRange the range to adapt
///\param pFunc a unary function or function object which performs the transformation
///
///This is useful to traverse a transformed version of a range without actually having to modify the values
///
///It is equivalent to `transform_iterator(range_begin(pRange),pFunc)`
template<typename tRange,typename tFunc>
inline ITransform<ORangeIterator<tRange>,tFunc> transform_begin(const tRange &pRange,const tFunc &pFunc)
{
    return {range_begin(pRange),pFunc};
}

///returns an \ref ITransform object pointing past the end of the range \p pRange
///
///\param pRange the range to adapt
///\param pFunc a unary function or function object which performs the transformation
///
///This is useful to traverse a transformed version of a range without actually having to modify the values
///
///It is equivalent to `transform_iterator(range_end(pRange),pFunc)`
template<typename tRange,typename tFunc>
inline ITransform<ORangeIterator<tRange>,tFunc> transform_end(const tRange &pRange,const tFunc &pFunc)
{
    return {range_end(pRange),pFunc};
}

///returns a range that can be used to access a transformed versions of the elements in another range
///
///\param pB the beginning of the original range
///\param pE the end of the original range
///\param pFunc a unary function or function object which performs the transformation
///
///The range will contain the elements resulting from applying pFunc by the elements in the range [\p pB,\p pE)
///
///it is equivalent to `range(transform_iterator(pB,pFunc),transform_iterator(pE,pFunc))`
template<typename tSub,typename tFunc>
inline CRange<ITransform<tSub,tFunc>> transform_range(const tSub &pB,const tSub &pE,const tFunc &pFunc)
{
    return {{pB,pFunc},{pE,pFunc}};
}

///returns a range that can be used to access a transformed versions of the elements in another range
///
///\param pRange
///\param pFunc a unary function or function object which performs the transformation
///
///The range will contain the elements resulting from applying pFunc by the elements in the original range
///
///it is equivalent to <tt>range(transform_begin(pRange,pFunc),transform_end(pRange,pFunc))</tt>
///
///For example,
///~~~~~~~~~~~~~~~~
///void print_numbers_plus(const std::vector<int> &pVector,int pN)
///{
///    auto lRange=transform_range(pVector,[pN](int pI)->int{return pI+pN});
///    std::copy(lRange.begin(),lRange.end(),io::ITextWriter<int>(gOut," "));
///}
///~~~~~~~~~~~~~~~~
///would print the elements in pVector, increased by pN.
template<typename tRange,typename tFunc>
inline auto transform_range(const tRange &pRange,const tFunc &pFunc)
                -> decltype(transform_range(range_begin(pRange),range_end(pRange),pFunc))
{
    return transform_range(range_begin(pRange),range_end(pRange),pFunc);
}

///\cond
namespace detail {

    template<typename tFunc>
    class CTransformForwarder
    {
    public:
        CTransformForwarder(const tFunc &pFunc)
            :   mFunc(pFunc)
        {}

        const tFunc &Func() const {   return mFunc;   }

    private:
        const tFunc &mFunc;
    };


    template<typename tRange,typename tFunc>
    inline auto operator|(const tRange &pR,const CTransformForwarder<tFunc> &pF) -> decltype(transform_range(pR,pF.Func()))
    {
        return transform_range(pR,pF.Func());
    }
    
/*namespace detail*/ }
///\endcond

///returns an object that can be used to transform ranges
///
///The only safe way to use the returned object is to use operator| to filter a range, as in:
///
///~~~~~~~~
///for(char i : "AAA" | transform(fn::AToLower<char>()))
///    msg_info() << i;
///~~~~~~~~
///
///which will print `aaa`
template<typename tFunc>
inline detail::CTransformForwarder<tFunc> transform(const tFunc &pFunc)
{
    return {pFunc};
}

namespace fn {
    ///returns the range transformed by the unary transform \p pTransform
    template<typename tRange,typename tFunc>
    OEnableIfTag<tFunc,GUnaryTransform,CRange<ITransform<ORangeIterator<tRange>,tFunc>>>
    operator|(const tRange &pRange,const tFunc &pFunc)
    {
        return transform_range(pRange,pFunc);
    }
/*namespace fn*/ }

//@}

///@}

/*namespace buola*/ }

#endif
